package git.bds.nyc.applet.api.service.order;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.rabbitmq.client.Channel;
import git.bds.nyc.communal.mapper.mp.ContractOrderMapper;
import git.bds.nyc.communal.model.domain.ContractOrder;
import git.bds.nyc.communal.model.dto.OrderDTO;
import git.bds.nyc.communal.util.Base64Util;
import git.bds.nyc.enums.OrderType;
import git.bds.nyc.enums.ProductType;
import git.bds.nyc.exception.BusinessException;
import git.bds.nyc.framework.file.minio.MinioConfig;
import git.bds.nyc.framework.file.minio.MinioUtil;
import git.bds.nyc.nyc.rabbitmq.RabbitMqConfig;
import git.bds.nyc.page.PageParam;
import git.bds.nyc.page.PageResult;
import git.bds.nyc.product.mapper.mp.CorpProcessingProductMapper;
import git.bds.nyc.product.mapper.mp.primary.corp.CorpPrimaryProductMapper;
import git.bds.nyc.product.mapper.mp.primary.farmer.FarmerPrimaryProductMapper;
import git.bds.nyc.product.model.domain.CorpPrimaryProduct;
import git.bds.nyc.product.model.domain.CorpProcessingProduct;
import git.bds.nyc.product.model.domain.FarmerPrimaryProduct;
import git.bds.nyc.result.ResultCode;
import git.bds.nyc.util.DecimalUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * @author 成大事
 * @since 2023/2/3 16:22
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class OrderServiceImpl implements OrderService{

    private final FarmerPrimaryProductMapper farmerPrimaryProductMapper;

    private final CorpProcessingProductMapper corpProcessingProductMapper;

    private final CorpPrimaryProductMapper corpPrimaryProductMapper;

    private final ContractOrderMapper contractOrderMapper;

    private final MinioConfig minioConfig;

    private final MinioUtil minioUtil;

    private final AmqpTemplate rabbitTemplate;

    /**
     * 下单
     *
     * @param orderDTO 订单dto
     * @return {@link Boolean}
     */
    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Boolean placeOrder(OrderDTO orderDTO) {
        //买家id
        long buyerId = StpUtil.getLoginIdAsLong();
        Long productId = orderDTO.getProductId();
        Integer type = orderDTO.getType();
        BigDecimal orderWeight = orderDTO.getOrderWeight();
        BigDecimal unitPrice = null;
        String productSpecies = null;
        String productVariety = null;
        String productCover = null;
        Long sellerContactInfoId = null;
        Long sellerId = null;
        if(ProductType.FARMER_PRIMARY.getValue().equals(type)){
            FarmerPrimaryProduct farmerPrimaryProduct = farmerPrimaryProductMapper.selectOne(new LambdaQueryWrapper<FarmerPrimaryProduct>()
                    .select(
                            FarmerPrimaryProduct::getId,
                            FarmerPrimaryProduct::getProductSpecies,
                            FarmerPrimaryProduct::getUserId,
                            FarmerPrimaryProduct::getProductVariety,
                            FarmerPrimaryProduct::getProductPrice,
                            FarmerPrimaryProduct::getProductWeight,
                            FarmerPrimaryProduct::getContactInfoId,
                            FarmerPrimaryProduct::getProductCover
                    )
                    .eq(FarmerPrimaryProduct::getId,productId)
            );
            //有数据
            if(farmerPrimaryProduct == null){
                throw new BusinessException(ResultCode.NOT_EXIST.getCode(),ResultCode.NOT_EXIST.getMessage());
            }
            //不能下单自己发布的
            if(farmerPrimaryProduct.getUserId().equals(buyerId)){
                throw new BusinessException(ResultCode.NOT_BUY_MYSELF.getCode(), ResultCode.NOT_BUY_MYSELF.getMessage());
            }
            //如果库存量不够
            if(Boolean.TRUE.equals(DecimalUtils.lessThan(farmerPrimaryProduct.getProductWeight(),orderWeight))){
                throw new BusinessException(ResultCode.INSUFFICIENT_STOCK.getCode(),ResultCode.INSUFFICIENT_STOCK.getMessage());
            }

            // 更新产品表里面的数据
            farmerPrimaryProduct.setProductWeight(DecimalUtils.subtract(farmerPrimaryProduct.getProductWeight(),orderWeight));
            farmerPrimaryProductMapper.updateById(farmerPrimaryProduct);

            unitPrice = farmerPrimaryProduct.getProductPrice();
            productSpecies = farmerPrimaryProduct.getProductSpecies();
            productVariety = farmerPrimaryProduct.getProductVariety();
            productCover = farmerPrimaryProduct.getProductCover();
            sellerContactInfoId = farmerPrimaryProduct.getContactInfoId();
            sellerId = farmerPrimaryProduct.getUserId();
        }else if(ProductType.CORP_PRIMARY.getValue().equals(type)){
            CorpPrimaryProduct corpPrimaryProduct = corpPrimaryProductMapper.selectOne(new LambdaQueryWrapper<CorpPrimaryProduct>()
                    .select(
                            CorpPrimaryProduct::getId,
                            CorpPrimaryProduct::getProductSpecies,
                            CorpPrimaryProduct::getUserId,
                            CorpPrimaryProduct::getProductVariety,
                            CorpPrimaryProduct::getProductPrice,
                            CorpPrimaryProduct::getProductWeight,
                            CorpPrimaryProduct::getProductCover
                    )
                    .eq(CorpPrimaryProduct::getId,productId)
            );
            if(ObjectUtil.isNull(corpPrimaryProduct)){
                throw new BusinessException(ResultCode.NOT_EXIST.getCode(),ResultCode.NOT_EXIST.getMessage());
            }
            //不能下单自己发布的
            if(corpPrimaryProduct.getUserId().equals(buyerId)){
                throw new BusinessException(ResultCode.NOT_BUY_MYSELF.getCode(), ResultCode.NOT_BUY_MYSELF.getMessage());
            }
            //如果库存量不够
            if(Boolean.TRUE.equals(DecimalUtils.lessThan(corpPrimaryProduct.getProductWeight(),orderWeight))){
                throw new BusinessException(ResultCode.INSUFFICIENT_STOCK.getCode(),ResultCode.INSUFFICIENT_STOCK.getMessage());
            }
            // 更新产品表里面的数据
            corpPrimaryProduct.setProductWeight(DecimalUtils.subtract(corpPrimaryProduct.getProductWeight(),orderWeight));
            corpPrimaryProductMapper.updateById(corpPrimaryProduct);

            unitPrice = corpPrimaryProduct.getProductPrice();
            productSpecies = corpPrimaryProduct.getProductSpecies();
            productVariety = corpPrimaryProduct.getProductVariety();
            productCover = corpPrimaryProduct.getProductCover();
            sellerContactInfoId = corpPrimaryProduct.getContactInfoId();
            sellerId = corpPrimaryProduct.getUserId();
        }else if(ProductType.CORP_PROCESSING.getValue().equals(type)){
            CorpProcessingProduct corpProcessingProduct = corpProcessingProductMapper.selectOne(new LambdaQueryWrapper<CorpProcessingProduct>()
                    .select(
                            CorpProcessingProduct::getId,
                            CorpProcessingProduct::getProductSpecies,
                            CorpProcessingProduct::getUserId,
                            CorpProcessingProduct::getProductVariety,
                            CorpProcessingProduct::getProductPrice,
                            CorpProcessingProduct::getProductWeight,
                            CorpProcessingProduct::getProductCover
                    )
                    .eq(CorpProcessingProduct::getId,productId)
            );
            if(corpProcessingProduct == null){
                throw new BusinessException(ResultCode.NOT_EXIST.getCode(),ResultCode.NOT_EXIST.getMessage());
            }
            //不能下单自己发布的
            if(corpProcessingProduct.getUserId().equals(buyerId)){
                throw new BusinessException(ResultCode.NOT_BUY_MYSELF.getCode(), ResultCode.NOT_BUY_MYSELF.getMessage());
            }
            //如果库存量不够
            if(Boolean.TRUE.equals(DecimalUtils.lessThan(corpProcessingProduct.getProductWeight(),orderWeight))){
                throw new BusinessException(ResultCode.INSUFFICIENT_STOCK.getCode(),ResultCode.INSUFFICIENT_STOCK.getMessage());
            }

            // 更新产品表里面的数据
            corpProcessingProduct.setProductWeight(DecimalUtils.subtract(corpProcessingProduct.getProductWeight(),orderWeight));
            corpProcessingProductMapper.updateById(corpProcessingProduct);

            unitPrice = corpProcessingProduct.getProductPrice();
            productSpecies = corpProcessingProduct.getProductSpecies();
            productVariety = corpProcessingProduct.getProductVariety();
            productCover = corpProcessingProduct.getProductCover();
            sellerContactInfoId = corpProcessingProduct.getContactInfoId();
            sellerId = corpProcessingProduct.getUserId();
        }
        //获取签名的字节流
        InputStream inputStream = Base64Util.generateImageStream(orderDTO.getBuyerSignature());
        String signPath = minioUtil.uploadSign(minioConfig.getBucketName(), inputStream, buyerId);
        ContractOrder contractOrder = new ContractOrder();
        contractOrder.setSellerId(sellerId);
        contractOrder.setBuyerId(buyerId);
        contractOrder.setProductId(productId);
        contractOrder.setProductSpecies(productSpecies);
        contractOrder.setProductVariety(productVariety);
        contractOrder.setProductCover(productCover);
        contractOrder.setType(type);
        contractOrder.setSellerContactInfoId(sellerContactInfoId);
        contractOrder.setBuyerContactInfoId(orderDTO.getBuyerContactInfoId());
        //买家签名地址
        contractOrder.setBuyerSignature(signPath);
        contractOrder.setUnitPrice(unitPrice);
        contractOrder.setOrderWeight(orderWeight);
        contractOrder.setRemark(orderDTO.getRemark());
        log.info("将订单编号发送到订单队列中");
        contractOrderMapper.insert(contractOrder);
        rabbitTemplate.convertAndSend(RabbitMqConfig.ORDER_EXCHANGE,RabbitMqConfig.ORDER_ROUTING,contractOrder.getId());
        return true;
    }

    /**
     * 判断是否在规定时间内支付
     *
     * @param channel     频道
     * @param deliveryTag 交货标签
     * @param message     消息
     * @throws IOException IOException
     */
    @RabbitListener(queues = RabbitMqConfig.ORDER_DEAD_QUEUE)
    @Transactional(rollbackFor = Exception.class)
    public void service(Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, @Payload String message) throws IOException {

        ContractOrder order = contractOrderMapper.selectOne(new LambdaQueryWrapper<ContractOrder>()
                .select(ContractOrder::getId,ContractOrder::getOrderStatus, ContractOrder::getOrderWeight,ContractOrder::getType,ContractOrder::getProductId)
                .eq(ContractOrder::getId, message));
        if (order != null) {
            Integer type = order.getType();
            BigDecimal orderWeight = order.getOrderWeight();
            Long productId = order.getProductId();
            if (OrderType.UNSIGNED.getValue().equals(order.getOrderStatus())) {
                log.info("超时订单: " + message);
                // 更新订单的状态为超时
                order.setOrderStatus(OrderType.TIME_OUT.getValue());
                contractOrderMapper.updateById(order);
                // 将下单的重量重新加回
                if(ProductType.FARMER_PRIMARY.getValue().equals(type)){
                    farmerPrimaryProductMapper.restoreOriginalWeight(productId,orderWeight);
                }else if(ProductType.CORP_PRIMARY.getValue().equals(type)){
                    corpPrimaryProductMapper.restoreOriginalWeight(productId,orderWeight);
                }else if(ProductType.CORP_PROCESSING.getValue().equals(type)){
                    corpProcessingProductMapper.restoreOriginalWeight(productId,orderWeight);
                }
            } else {
                log.info("订单: " + message + "已支付");
            }
        }
        channel.basicAck(deliveryTag, false);
    }

    /**
     * 按页面获取我订单
     *
     * @param pageParam 页面参数
     * @return {@link PageResult}<{@link ContractOrder}>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PageResult<ContractOrder> getMyOrderPage(PageParam pageParam) {
        Page<ContractOrder> page = contractOrderMapper.selectPage(new Page<>(pageParam.getPageNo(), pageParam.getPageSize()),
                new LambdaQueryWrapper<ContractOrder>()
                        .select(
                                ContractOrder::getId,
                                ContractOrder::getProductId,
                                ContractOrder::getProductSpecies,
                                ContractOrder::getProductVariety,
                                ContractOrder::getProductCover,
                                ContractOrder::getOrderWeight,
                                ContractOrder::getCreateTime,
                                ContractOrder::getUnitPrice,
                                ContractOrder::getOrderStatus,
                                ContractOrder::getType
                        )
                        .eq(ContractOrder::getBuyerId, StpUtil.getLoginIdAsLong())
                        .orderByAsc(ContractOrder::getCreateTime));
        return new PageResult<>(page.getRecords(),page.getTotal());
    }

    /**
     * 按id删除未签名订单
     *
     * @param type 类型
     * @param id   订单id
     * @return {@link Boolean}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delOrderById(int type, Long id) {
        long userId = StpUtil.getLoginIdAsLong();
        ContractOrder contractOrder = contractOrderMapper.selectOne(new LambdaQueryWrapper<ContractOrder>()
                        .select(ContractOrder::getContractUrl,ContractOrder::getBuyerSignature,ContractOrder::getSellerSignature)
                .eq(ContractOrder::getId, id)
                .eq(ContractOrder::getBuyerId, userId)
                .eq(ContractOrder::getOrderStatus, type));
        if(contractOrder == null){
            throw new BusinessException(ResultCode.NOT_EXIST.getCode(),ResultCode.NOT_EXIST.getMessage());
        }
        List<String> filePathList = Arrays.asList(contractOrder.getBuyerSignature(), contractOrder.getSellerSignature(), contractOrder.getContractUrl());
        if(OrderType.SUCCESSFUL_TRADE.getValue().equals(type) || OrderType.SIGNED.getValue().equals(type)){
            minioUtil.removeFiles(minioConfig.getBucketName(),filePathList);
        }
        return contractOrder.deleteById(id);
    }


    /**
     * 按类型获取我订单页面
     *
     * @param pageParam 页面参数
     * @param type      类型
     * @return {@link PageResult}<{@link ContractOrder}>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PageResult<ContractOrder> getMyOrderPageByType(PageParam pageParam, int type) {
        Page<ContractOrder> page = contractOrderMapper.selectPage(new Page<>(pageParam.getPageNo(), pageParam.getPageSize()),
                new LambdaQueryWrapper<ContractOrder>()
                        .select(
                                ContractOrder::getId,
                                ContractOrder::getProductId,
                                ContractOrder::getProductSpecies,
                                ContractOrder::getProductVariety,
                                ContractOrder::getProductCover,
                                ContractOrder::getOrderWeight,
                                ContractOrder::getCreateTime,
                                ContractOrder::getUnitPrice,
                                ContractOrder::getOrderStatus,
                                ContractOrder::getType
                        )
                        .eq(ContractOrder::getBuyerId, StpUtil.getLoginIdAsLong())
                        .eq(ContractOrder::getOrderStatus, type)
                        .orderByAsc(ContractOrder::getCreateTime));
        return new PageResult<>(page.getRecords(),page.getTotal());
    }

    /**
     * 获取指定时间段页面顺序
     *
     * @param pageParam 页面参数
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return {@link PageResult}<{@link ContractOrder}>
     */
    @Override
    public PageResult<ContractOrder> getOrderOfTheSpecifiedTimePeriodPage(PageParam pageParam, LocalDateTime startTime, LocalDateTime endTime) {
        Page<ContractOrder> page = contractOrderMapper.selectPage(new Page<>(pageParam.getPageNo(), pageParam.getPageSize()),
                new LambdaQueryWrapper<ContractOrder>()
                        .select(
                                ContractOrder::getId,
                                ContractOrder::getProductId,
                                ContractOrder::getProductSpecies,
                                ContractOrder::getProductVariety,
                                ContractOrder::getProductCover,
                                ContractOrder::getOrderWeight,
                                ContractOrder::getCreateTime,
                                ContractOrder::getUnitPrice,
                                ContractOrder::getOrderStatus,
                                ContractOrder::getType
                        )
                        .eq(ContractOrder::getBuyerId, StpUtil.getLoginIdAsLong())
                        .between(ContractOrder::getCreateTime, startTime.withHour(0).withMinute(0).withSecond(0),endTime.withHour(23).withMinute(59).withSecond(59))
                        .orderByAsc(ContractOrder::getCreateTime));
        return new PageResult<>(page.getRecords(),page.getTotal());
    }


    /**
     * 按订单id取消未签名订单
     *
     * @param id 订单id
     * @return {@link Boolean}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancelUnsignedOrderById(Long id) {
        long userId = StpUtil.getLoginIdAsLong();
        ContractOrder contractOrder = contractOrderMapper.selectOne(new LambdaQueryWrapper<ContractOrder>()
                .select(ContractOrder::getBuyerSignature,ContractOrder::getOrderStatus)
                .eq(ContractOrder::getId, id)
                .eq(ContractOrder::getBuyerId, userId));
        if(contractOrder == null){
            throw new BusinessException(ResultCode.NOT_EXIST.getCode(),ResultCode.NOT_EXIST.getMessage());
        }
        if (contractOrder.getOrderStatus().equals(OrderType.UNSIGNED.getValue())){
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR.getCode(),ResultCode.ORDER_STATUS_ERROR.getMessage());
        }
        return contractOrder.deleteById(id) && minioUtil.removeFile(minioConfig.getBucketName(),contractOrder.getBuyerSignature());
    }
}
